﻿module Preprocessor.Preprocessor

open Cabs
open Clexer
open Lexer
open PrintUtils
open System.IO
open History
open Prep
open Include
open Utils

let rec preprocessNext (context:Context) (prep:Prep) lexemes =
     seq {
        let value =  prep.GetNextLexeme lexemes
        if value.IsSome then
         match value.Value with
            | EOF(_) -> ()
            | DIRECTIVE_DEFINE x -> 
                 let save = Define.saveDefine context prep lexemes
                 addHistory prep ("#define " + save) "" x true
                 yield! preprocessNext context prep lexemes 
            | IDENT(a, b) ->
                match context.DefinesMap.TryGetValue' a with
                | None -> 
                    //addHistory prep "" a b false    
                    yield IDENT(a, b)
                | Some value ->
                    Define.trySubstituteDefine prep a b value lexemes
                yield! preprocessNext context prep lexemes
            | DIRECTIVE_INCLUDE(x, y) ->
                let file = lexicInclude context prep  x
                yield! preprocessNext context (new Prep(file)) (runLexer file)
                finish ()
                addHistory prep ("#include " + x) "" y true
                yield! preprocessNext context prep lexemes
            | DIRECTIVE_INCLUDENEXT(x,y) -> 
                let file = lexicIncludeNext context prep  x
                yield! preprocessNext context (new Prep(file)) (runLexer file)
                finish ()
                addHistory prep ("#include " + x) "" y true
                yield! preprocessNext context prep lexemes
            | DIRECTIVE_UNDEF(x, y) -> addHistory prep ("#undef " + x) "" y true
                                       context.DefinesMap.Remove(x) |> ignore
                                       yield! preprocessNext context prep lexemes
            | DIRECTIVE_IFDEF(x,y) -> addHistory prep ("#ifdef " + x) "" y true
                                      if not (context.DefinesMap.ContainsKey x) then
                                           IfDef.skipLexemes context prep lexemes false
                                      yield! preprocessNext context prep lexemes
            | DIRECTIVE_IFNDEF(x,y) -> addHistory prep ("#ifndef " + x) "" y true
                                       if (context.DefinesMap.ContainsKey x) then
                                            IfDef.skipLexemes context prep lexemes false
                                       yield! preprocessNext context prep lexemes
            | DIRECTIVE_IF(y) ->   addHistory prep ("#if") "" y true
                                   IfDef.convert context (new Prep(prep.SourceFile)) (prep.takeWhile (fun lex -> (PrintUtils.GetLoc lex).lineno = y.lineno) lexemes)
                                   yield! preprocessNext context prep lexemes
            | DIRECTIVE_ELSE(y) 
            | DIRECTIVE_ELIF(y) -> addHistory prep ("#else ") "" y true
                                   IfDef.skipLexemes context prep lexemes false
                                   yield! preprocessNext context prep lexemes
            | DIRECTIVE_ENDIF(x) ->  addHistory prep ("#endif") "" x true
                                     yield! preprocessNext context prep lexemes
            | COMMENT_ONELINE(x,y) -> addHistory prep ("//" + x) "" y true
                                      yield! preprocessNext context prep lexemes
            | COMMENT(x,y) -> addHistory prep ("/*" + x + "*/") "" y true
                              yield! preprocessNext context prep lexemes
            | x ->
                 addHistory prep "" (TokenToString x) (GetLoc x) false
                 yield x
                 yield! preprocessNext context prep lexemes
     }


let preprocessFile filename = 
    
    let lexemes = runLexer filename
    let prep = new Prep(filename)
    let outdir = @"out\"
    if Directory.Exists outdir then
        Directory.Delete(outdir, true)
    Directory.CreateDirectory outdir |> ignore
    use writer = new StreamWriter(outdir + Path.GetFileNameWithoutExtension(filename) + ".i")
    use historer = new StreamWriter(outdir + Path.GetFileNameWithoutExtension(filename) + ".history")
    let context = new Prep.Context()

    context.SearchDirs.Add("C:\\creen\\cygwin\\lib\\gcc\\i686-pc-cygwin\\4.3.4\\include")
    for dir in Directory.GetDirectories("C:\\creen\\cygwin\\lib\\gcc\\i686-pc-cygwin\\4.3.4\\include", "*", SearchOption.AllDirectories) do
        context.SearchDirs.Add(dir)
    try 
    (
        Seq.iter (fun x -> fprintfn writer "%A" x) (preprocessNext context prep lexemes)
        List.iter (fun x -> fprintfn historer "%A" x) (List.rev prep.History)
        writer.Close()
        historer.Close()
        finish()
    )
    with x -> fprintf writer "%A" x
              writer.Close()
              historer.Close()   
              finish()
